package net.lab1024.sa.admin.module.business.traffic.aibi.service;

import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONException;

import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import net.lab1024.sa.admin.module.business.traffic.aibi.domain.entity.AibiCarQueueEntity;
import net.lab1024.sa.admin.module.business.traffic.aibi.domain.model.AibiCarQueueLaneMessage;
import net.lab1024.sa.admin.module.business.traffic.aibi.domain.model.AibiCarQueueMessage;
import net.lab1024.sa.admin.module.business.traffic.config.AibiCameraProperties;

/**
 * 艾比摄像机数据处理服务
 */
@Slf4j
@Service
public class AibiCameraProcessingService {

    private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
    private static final ZoneId ZONE_ID = ZoneId.systemDefault();

    @Resource
    private AibiCameraProperties cameraProperties;

    @Resource
    private AibiCarQueueService carQueueService;

    @Resource
    private AibiCameraStatusService cameraStatusService;

    @Resource
    private AibiLaneModeService laneModeService;

    /**
     * 处理 TCP 报文
     */
    public void handlePayload(String payload) {
        if (StringUtils.isBlank(payload)) {
            return;
        }
        List<AibiCarQueueMessage> messages;
        try {
            messages = JSON.parseArray(payload, AibiCarQueueMessage.class);
        } catch (JSONException ex) {
            log.error("解析摄像机数据失败 payload={}", payload, ex);
            return;
        }
        if (messages == null || messages.isEmpty()) {
            return;
        }
        messages.forEach(message -> processSingleMessage(message, payload));
    }

    private void processSingleMessage(AibiCarQueueMessage message, String rawPayload) {
        if (message == null || message.getLaneMessages() == null) {
            return;
        }
        String cameraCode = StringUtils.defaultIfBlank(message.getRoadSectionCode(), cameraProperties.getDevice().getCameraCode());

        List<AibiCarQueueEntity> entities = new ArrayList<>(message.getLaneMessages().size());

        LocalDateTime startTime = parseDateTime(message.getStartTime());
        LocalDateTime endTime = parseDateTime(message.getEndTime());

        int leftQueue = 0;
        int straightQueue = 0;
        LocalDateTime sampleTime = null;

        for (int index = 0; index < message.getLaneMessages().size(); index++) {
            AibiCarQueueLaneMessage laneMessage = message.getLaneMessages().get(index);
            if (laneMessage == null) {
                continue;
            }

            LocalDateTime laneSampleTime = parseTimestamp(laneMessage.getTimestamp());
            if (laneSampleTime != null && (sampleTime == null || laneSampleTime.isAfter(sampleTime))) {
                sampleTime = laneSampleTime;
            }

            String laneType = resolveLaneType(message, laneMessage, index);
            if ("LEFT".equals(laneType)) {
                leftQueue = optionalValue(laneMessage.getQueueLength());
            } else if ("STRAIGHT".equals(laneType)) {
                straightQueue = optionalValue(laneMessage.getQueueLength());
            }

            AibiCarQueueEntity entity = buildEntity(cameraCode, laneMessage, index, laneType, laneSampleTime, startTime, endTime, rawPayload);
            entities.add(entity);
        }

        if (sampleTime == null) {
            sampleTime = Optional.ofNullable(endTime).orElse(LocalDateTime.now());
        }

        double ratio = calculateRatio(leftQueue, straightQueue);

        carQueueService.saveBatch(entities);
        cameraStatusService.updateQueues(cameraCode, cameraProperties.getDevice().getRoadCode(),
                cameraProperties.getDevice().getDeviceId(), leftQueue, straightQueue, ratio, sampleTime);
        laneModeService.evaluateAndSwitch(cameraCode, ratio, sampleTime);
    }

    private String resolveLaneType(AibiCarQueueMessage message, AibiCarQueueLaneMessage laneMessage, int index) {
        AibiCameraProperties.Lane laneConfig = cameraProperties.getLane();
        String laneCode = laneMessage.getLaneCode();
        String leftLaneCode = laneConfig.getLeftLaneCode();
        String straightLaneCode = laneConfig.getStraightLaneCode();
        if (StringUtils.isNotBlank(leftLaneCode) && laneCode != null && laneCode.equalsIgnoreCase(leftLaneCode)) {
            return "LEFT";
        }
        if (StringUtils.isNotBlank(straightLaneCode) && laneCode != null && laneCode.equalsIgnoreCase(straightLaneCode)) {
            return "STRAIGHT";
        }
        if (StringUtils.isBlank(laneConfig.getLeftLaneCode()) && index == laneConfig.getLeftLaneIndex()) {
            return "LEFT";
        }
        if (StringUtils.isBlank(laneConfig.getStraightLaneCode()) && index == laneConfig.getStraightLaneIndex()) {
            return "STRAIGHT";
        }
        return "OTHER";
    }

    private AibiCarQueueEntity buildEntity(String cameraCode, AibiCarQueueLaneMessage laneMessage, int index, String laneType,
                                           LocalDateTime sampleTime, LocalDateTime startTime, LocalDateTime endTime, String rawPayload) {
        AibiCarQueueEntity entity = new AibiCarQueueEntity();
        entity.setCameraCode(cameraCode);
        entity.setRoadCode(cameraProperties.getDevice().getRoadCode());
        entity.setLaneCode(laneMessage.getLaneCode());
        entity.setLaneOrder(index);
        entity.setLaneType(laneType);
        entity.setQueueLength(optionalValue(laneMessage.getQueueLength()));
        entity.setVehicleCount(optionalValue(laneMessage.getTotalCount()));
        entity.setSampleTime(sampleTime);
        entity.setStartTime(startTime);
        entity.setEndTime(endTime);
        long messageTimestamp = sampleTime == null ? System.currentTimeMillis() / 1000 : sampleTime.atZone(ZONE_ID).toEpochSecond();
        String laneCodePart = laneMessage.getLaneCode();
        if (laneCodePart == null) {
            laneCodePart = String.valueOf(index);
        }
        entity.setMessageId(String.format("%s_%s_%d", cameraCode, laneCodePart, messageTimestamp));
        entity.setRawData(rawPayload);
        return entity;
    }

    private LocalDateTime parseDateTime(String value) {
        if (StringUtils.isBlank(value)) {
            return null;
        }
        try {
            return LocalDateTime.parse(value, DATE_TIME_FORMATTER);
        } catch (Exception ignore) {
            // fall-through
        }
        return parseTimestamp(value);
    }

    private LocalDateTime parseTimestamp(String value) {
        if (StringUtils.isBlank(value)) {
            return null;
        }
        String trimmed = value.trim();
        try {
            long epoch = Long.parseLong(trimmed);
            if (trimmed.length() > 11) {
                return LocalDateTime.ofInstant(Instant.ofEpochMilli(epoch), ZONE_ID);
            }
            return LocalDateTime.ofInstant(Instant.ofEpochSecond(epoch), ZONE_ID);
        } catch (NumberFormatException ex) {
            try {
                return LocalDateTime.parse(trimmed, DATE_TIME_FORMATTER);
            } catch (Exception ignore) {
                log.debug("时间格式无法解析 value={}", value);
            }
        }
        return null;
    }

    private int optionalValue(Integer value) {
        return value == null ? 0 : Math.max(value, 0);
    }

    private double calculateRatio(int leftQueue, int straightQueue) {
        if (leftQueue <= 0 && straightQueue <= 0) {
            return 0D;
        }
        if (straightQueue <= 0) {
            return leftQueue > 0 ? Double.POSITIVE_INFINITY : 0D;
        }
        return leftQueue * 1.0D / doubleValue(straightQueue);
    }

    private double doubleValue(int value) {
        return (double) value;
    }
}

